---
title: "SSRM Row Grouping"
enterprise: true
---

This section covers Row Grouping in the Server-Side Row Model (SSRM).

## Enabling Row Grouping

Row Grouping is enabled in the grid via the `rowGroup` column definition attribute.
The example below shows how to group rows by 'country':

```{% frameworkTransform=true %}
const gridOptions = {
    columnDefs: [
        { field: 'country', rowGroup: true },
        { field: 'sport' },
        { field: 'year' },
    ]
}
```

For more configuration details see the section on [Row Grouping](./grouping/).

## Server Side Row Grouping

The actual grouping of rows is performed on the server when using the SSRM. When the grid needs more rows it makes a
request via `getRows(params)` on the [Server-Side Datasource](./server-side-model-datasource) with metadata containing
grouping details.

The properties relevant to Row Grouping in the request are shown below:

```ts
type IServerSideGetRowsRequest = {
    // row group columns
    rowGroupCols: ColumnVO[];

    // what groups the user is viewing
    groupKeys: string[];
    
    // ... // other params
}
```

Note in the snippet above the property `rowGroupCols` contains all the columns (dimensions) the grid is
grouping on, e.g. 'Country', 'Year'. The property `groupKeys` contains the list of group keys selected,
e.g. `['Argentina', '2012']`.

The example below demonstrates server-side Row Grouping. Note the following:

* **Country** and **Sport** columns have `rowGroup=true` defined on their column definitions. This tells the grid there are two levels of grouping, one for Country and one for Sport.
* The `rowGroupCols` and `groupKeys` properties in the request are used by the server to perform grouping.
* Open the browser's dev console to view the request supplied to the datasource.

{% gridExampleRunner title="Row Grouping" name="row-grouping"  exampleHeight=590 /%}

## Open by Default

It is possible to have rows open as soon as they are loaded. To do this implement the grid callback `isServerSideGroupOpenByDefault`.

{% apiDocumentation source="grid-options/properties.json" section="serverSideRowModel" names=["isServerSideGroupOpenByDefault"] /%}

```js
// Example implementation
function isServerSideGroupOpenByDefault(params) {
    var rowNode = params.rowNode;
    var isZimbabwe = rowNode.field == 'country' && rowNode.key == 'Zimbabwe';
    return isZimbabwe;
}
```

{% note %}
Server-Side Open By Default requires [Row IDs](./server-side-model-configuration/#providing-row-ids) to be supplied to the grid.
{% /note %}

It may also be helpful to use the [Row Node](./row-object/) API `getRoute()` to inspect the route of a row node.

{% apiDocumentation source="row-object/properties.json" section="serverSide" names=["getRoute"] /%}

Below shows `isServerSideGroupOpenByDefault()` and `getRoute` in action. Note the following:

* The callback opens the following routes as soon as those routes are loaded:
  * **\[Zimbabwe]**
  * **\[Zimbabwe, Swimming]**
  * **\[United States, Swimming]**
* Note **\[Zimbabwe]** and **\[Zimbabwe, Swimming]** are visibly open by default.
* Note **\[United States, Swimming]** is not visibly open by default, as the parent group 'United States' is not open. However when 'United States' is opened, it's 'Swimming' group is opened by default.
* Selecting a group row and clicking 'Route of Selected' prints the route to the selected node to the developer console.

{% gridExampleRunner title="Open by Default" name="open-by-default" /%}

## Group Total Rows

To enable [Group Total Rows](./aggregation-total-rows/), set the `groupTotalRow` property to 'top' or 'bottom'. Note that the grand total row is not supported by the SSRM.

{% gridExampleRunner title="Group Totals" name="group-footer" /%}

Group total rows can also be used with `groupDisplayType='multipleColumns'`, as demonstrated in the example below.

{% gridExampleRunner title="Multiple Group Columns and Footers" name="group-footer-multiple-cols" /%}

## Hide Open Parents

In some configurations it may be desired for the group row to be hidden when expanded, this can be achieved by setting the `groupHideOpenParents` property to true.

The example below has been styled in a way that demonstrates the behaviour of the groups. Note how upon expanding a group, the group row is replaced by the first
of its children, and only when collapsed is the group row is shown again.

{% gridExampleRunner title="Hide Open Parents" name="hide-open-parents" /%}

{% note %}
When `groupHideOpenParents=true` the Grid automatically disables the [Sticky Groups](./grouping-opening-groups/#prevent-sticky-groups) behaviour of the rows as well as [Full Width Loading](./component-loading-cell-renderer/#skeleton-loading).
{% /note %}

## Unbalanced Groups

To enable unbalanced groups in the SSRM, set the `groupAllowUnbalanced` property to true. This causes any group with a key of `''` to behave as if it is always expanded, and the group row to always be hidden.

{% gridExampleRunner title="Unbalanced Groups" name="unbalanced-groups" /%}

{% note %}
When using `groupAllowUnbalanced=true` it is important to remember that a row group still exists to contain the unbalanced nodes, this can be an important consideration when working with selection state, refreshing, or group paths.
This also means that there will be additional requests and delays in loading these unbalanced rows, as they do not belong to the parent row.
{% /note %}

## Expand All / Collapse All

Group rows can be expanded or collapsed using the `expandAll()` and `collapseAll()` grid API's. By default, these operations apply only to **loaded group rows** (not all groups). To expand/collapse **all groups**, including those not yet loaded, set `ssrmExpandAllAffectsAllRows: true` in your grid options.

The example below demonstrates this feature, note the following:

- First button expands all loaded group rows
- Checking the checkbox enables `ssrmExpandAllAffectsAllRows` in the grid options
- Now clicking the first button expands all group rows, including those not yet loaded
- Second button collapses all group rows

{% gridExampleRunner title="expand-all-affects-all-rows" name="expand-all-affects-all-rows"  exampleHeight=540 /%}


To open only specific groups, e.g. only groups at the top level, then use the `forEachNode()` callback and open / close the row using `setExpanded()` as follows:

```{% frameworkTransform=true %}
// Expand all top level row nodes
api.forEachNode(node => {
    if (node.group && node.level == 0) {
        node.setExpanded(true);
    }
});
```

The example below demonstrates these techniques. Note the following:

* Clicking 'Expand All' expands all loaded group rows. Doing this when the grid initially loads expands all Year groups. Clicking it a second time (after Year groups have loaded) causes all Year groups as well as their children Country groups to be expanded - this is a heavier operation with 100's of rows to expand.

* Clicking 'Collapse All' collapses all rows.

* Clicking 'Expand Top Level Only' expands Years only, even if more group rows are loaded.

{% gridExampleRunner title="Expand All" name="expand-all" /%}

## Providing Child Counts

By default, the grid does not show row counts beside the group names. If you do want row counts, you need to implement the `getChildCount(dataItem)` callback for the grid. The callback provides you with the row data; it is your application's responsibility to know what the child row count is. The suggestion is you set this information into the row data item you provide to the grid.

{% apiDocumentation source="grid-options/properties.json" section="serverSideRowModel" names=["getChildCount"] /%}

```{% frameworkTransform=true %}
const gridOptions = {
    getChildCount: data => {
        // here child count is stored in the 'childCount' property
        return data.childCount;
    }
}
```

{% gridExampleRunner title="Child Counts" name="child-counts"  exampleHeight=590 /%}

## Group via Value Getter

It is possible the data provided has composite objects, in which case it's more difficult for the grid to extract group names. This can be worked with using value getters or embedded fields (i.e. the field attribute has dot notation).

In the example below, all rows are modified so that the rows look something like this:

```js
// sample contents of row data
const rowData = {
    // country field is complex object
    country: {
        name: 'Ireland',
        code: 'IRE'
    },

    // year field is complex object
    year: {
        name: '2012',
        shortName: "'12"
    },

    // other fields as normal
    ...
}
```

Then the columns are set up so that country uses a `valueGetter` that uses the field with dot notation, i.e. `data.country.name`

{% gridExampleRunner title="Complex Objects" name="complex-objects"  exampleHeight=590 /%}

## Filters

By default, changing filters fully purges the grid. Though, it can be configured to only refresh when the group has been directly impacted by enabling `serverSideOnlyRefreshFilteredGroups`. Be aware, this can mean your grid may have empty group rows. This is because the grid does not refresh the groups above the groups it deems impacted by the filter.

In the example below, note the following:

* Filtering by `Gold`, `Silver` or `Bronze` fully purges the grid, this is because they have aggregations applied.
* Applying a filter to the `Year` column does not purge the entire grid, and instead only refreshes the `Year` group rows.
* The example enables `serverSideOnlyRefreshFilteredGroups`, note that if you apply a filter to `Year` with the value `1900`, no leaf rows exist in any group.

{% gridExampleRunner title="Filtering" name="filtering"  exampleHeight=590 /%}

## Next Up

Continue to the next section to learn about [SSRM Pivoting](./server-side-model-pivoting/).
